/// ------------------------------------------------------------------------------------------------------------------------------------
///
/// MIT License
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
/// SOFTWARE.
///
/// Copyright (c) 2023 ycz. All rights reserved.
///
/// Created by ycz on 2023/12/5.
///
/// @file  y_modbus.c
///
/// @brief
///      y_modbus 一种用于嵌入式设备的简单 modbus 通信
///
/// ------------------------------------------------------------------------------------------------------------------------------------



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 头文件
/// ------------------------------------------------------------------------------------------------------------------------------------

#include "y_modbus.h"

#include <string.h>
#include <stdlib.h>
#include "y_sa.h"



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 宏定义
/// ------------------------------------------------------------------------------------------------------------------------------------

#ifdef _Y_LL_H
#    define Y_MALLOC y_ll_malloc
#    define Y_FREE   y_ll_free
#else
#    define Y_MALLOC malloc
#    define Y_FREE   free
#endif



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 私有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   消息分类处理
/// @param   [in] handle                   句柄
/// @param   [in] data                     数据
/// @param   [in] size                     大小
static void _y_modbus_slave_handle_msg(MODBUS_st *handle, uint8_t *data, uint16_t size) {

    // 断言
    YLOGA(handle->write);
    YLOGA(handle->reg_set);
    YLOGA(handle->reg_get);

    // 组装数据
    uint8_t  send_buf[261] = {0};
    uint16_t send_size     = 0;
    send_buf[0]            = y_sys_get_dev_num();  // 从机地址
    send_buf[1]            = data[1];              // 功能码

    // 解析数据
    switch (data[1]) {
        case 3:    // 读取多个寄存器的值
        case 4: {  // 读取多个寄存器的值

            // 判断是不是广播消息
            if (data[0] == 0) {
                return;  // 广播消息 不回应
            }

            // 解析数据
            uint16_t reg_addr = *(uint16_t *) y_utils_big_little_swap(&data[2], 2);
            uint16_t reg_num  = *(uint16_t *) y_utils_big_little_swap(&data[4], 2);
            if (reg_num > 128) {
                return;  // 超过一包所能承载的数据大小
            }
            // 组装返回数据
            send_buf[2] = reg_num * 2;  // 数据大小
            for (int i = 0; i < reg_num; ++i) {
                uint16_t reg_data = handle->reg_get(reg_addr + i);         // 获取寄存器的值
                y_utils_big_little_swap((uint8_t *) &reg_data, 2);         // 数据大小端装换
                memcpy(&send_buf[3 + (i * 2)], (uint8_t *) &reg_data, 2);  // 复制数据
            }
            // 获取发送数据大小
            send_size = 5 + send_buf[2];
        } break;

        case 6: {  // 写入单个寄存器的值
            // 解析数据
            uint16_t reg_addr = *(uint16_t *) y_utils_big_little_swap(&data[2], 2);
            uint16_t reg_data = *(uint16_t *) y_utils_big_little_swap(&data[4], 2);
            // 修改寄存器的值
            handle->reg_set(data[0], reg_addr, reg_data);
            reg_data = handle->reg_get(reg_addr);
            // 组装返回数据
            memcpy(&send_buf[2], y_utils_big_little_swap((uint8_t *) &reg_addr, 2), 2);  // 寄存器
            memcpy(&send_buf[4], y_utils_big_little_swap((uint8_t *) &reg_data, 2), 2);  // 寄存器数据
            // 获取发送数据大小
            send_size = 8;
        } break;

        case 16: {  // 写入多个寄存器的值
            // 解析数据
            uint16_t reg_addr = *(uint16_t *) y_utils_big_little_swap(&data[2], 2);
            uint16_t reg_num  = *(uint16_t *) y_utils_big_little_swap(&data[4], 2);
            // 修改寄存器的值
            for (int i = 0; i < reg_num; ++i) {
                uint16_t reg_data = *(uint16_t *) y_utils_big_little_swap(&data[7 + i * 2], 2);
                handle->reg_set(data[0], reg_addr + i, reg_data);
            }
            // todo: 升级的时候不回应
            if (reg_addr > 255) {
                return;
            }
            // 组装返回数据
            memcpy(&send_buf[2], y_utils_big_little_swap((uint8_t *) &reg_addr, 2), 2);  // 寄存器
            memcpy(&send_buf[4], y_utils_big_little_swap((uint8_t *) &reg_num, 2), 2);   // 寄存器数量
            // 获取发送数据大小
            send_size = 8;
        } break;

        default:
            return;  // 不识别的直接返回
    }

    // 获取 crc16
    uint16_t crc16 = y_utils_get_crc16(send_buf, send_size - 2);
    memcpy(&send_buf[send_size - 2], &crc16, 2);

    // 发送数据
    if (data[0] != 0X00) {  // 广播消息 不回应
        handle->write(send_buf, send_size);
    }
}

/// @brief   主机处理数据
/// @param   [in] handle                   句柄
static void _y_modbus_master_process(MODBUS_st *handle) {

    while (1) {

        // 读取一帧数据
        uint8_t recv_buf[1024] = {0};
        memset(recv_buf, 0, sizeof(recv_buf));
        if (y_ringbuf_read_only(handle->ringbuf, recv_buf, 5) != 5) {
            return;  // 数据不满足最小数 不在处理 等数据足够在处理
        }
        // 判断功能码
        switch (recv_buf[1]) {
            case 3:
            case 4: {
                // 判断当前数据是否足够
                if (y_ringbuf_get_used_size(handle->ringbuf) < recv_buf[2] + 5) {
                    y_ringbuf_delete_data(handle->ringbuf, 1);  // 数据不足 删除一个数据后重新读取
                    continue;                                   // 重新循环
                }

                // 再次读取数据
                uint16_t data_size = recv_buf[2] + 5;  // 获取数据大小
                y_ringbuf_read_only(handle->ringbuf, recv_buf, data_size);

                // 检验 crc16
                uint16_t crc16_now   = y_utils_get_crc16(recv_buf, data_size - 2);
                uint16_t crc16_frame = *(uint16_t *) &recv_buf[data_size - 2];
                if (crc16_now != crc16_frame) {
                    y_ringbuf_delete_data(handle->ringbuf, 1);  // 删除一个数据后重新读取
                    continue;
                } else {
                    y_ringbuf_delete_data(handle->ringbuf, data_size);  // 删除这一帧数据
                }

                // 放到外部进行业务处理
                if (handle->business) {
                    handle->business(recv_buf[0], recv_buf[1], &recv_buf[3], data_size - 5);
                }
            } break;

            case 6:
            case 16:
                y_ringbuf_delete_data(handle->ringbuf, 1);  // 删除一个数据后重新读取
                break;

            default:
                y_ringbuf_delete_data(handle->ringbuf, 1);  // 删除一个数据后重新读取
                continue;
        }
    }
}

/// @brief   从机处理数据
/// @param   [in] handle                   句柄
static void _y_modbus_slave_process(MODBUS_st *handle) {

    while (1) {

        uint8_t recv_buf[261] = {0};  // modbus协议 + 256个数据 最大为 261

        // 读取一帧数据
        if (y_ringbuf_read_only(handle->ringbuf, recv_buf, 8) != 8) {
            return;  // 数据不满足最小数 不在处理 等数据足够在处理
        }
        // 不是本机地址 或者 广播地址 0x00 不处理
        if (recv_buf[0] != y_sys_get_dev_num() && recv_buf[0] != 0X00) {
            y_ringbuf_delete_data(handle->ringbuf, 1);  // 删除一个数据后重新读取
            continue;
        }

        // 判断功能码 获取数据长度
        uint16_t data_size   = recv_buf[1] == 16 ? (9 + recv_buf[6]) : 8;  // 默认情况 8 个字节

        // 再次读取剩下的数据
        static uint8_t count = 0;
        if (data_size > 8) {
            if (y_ringbuf_read_only(handle->ringbuf, recv_buf, data_size) != data_size) {
                if (count++ > 10) {
                    y_ringbuf_delete_data(handle->ringbuf, 1);  // 删除一个数据后重新读取
                    count = 0;
                }
                return;  // 数据不满足最小数 不在处理 等数据足够在处理
            }
        } else {
            count = 0;
        }

        // 检验 crc16
        uint16_t crc16_now   = y_utils_get_crc16(recv_buf, data_size - 2);
        uint16_t crc16_frame = *(uint16_t *) &recv_buf[data_size - 2];
        if (crc16_now != crc16_frame) {
            y_ringbuf_delete_data(handle->ringbuf, 1);  // 删除一个数据后重新读取
            continue;
        } else {
            y_ringbuf_delete_data(handle->ringbuf, data_size);  // 删除这一帧数据
        }

        // 解析数据
        _y_modbus_slave_handle_msg(handle, recv_buf, data_size);
    }
}



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   打印 y_modbus 版本信息
void y_modbus_print_version() {
    YLOG_VERSION("y_modbus", Y_MODBUS_MAJOR, Y_MODBUS_MINOR, Y_MODBUS_PATCH);
}

/// @brief   异步读取多个寄存器
/// @param   [in] handle                   句柄
/// @param   [in] addr                     地址
/// @param   [in] reg                      寄存器
/// @param   [in] num                      寄存器个数
/// @retval  true                          成功
/// @retval  false                         失败
bool y_modbus_master_read_data_async(MODBUS_st *handle, uint8_t addr, uint16_t reg, uint16_t num) {

    // 断言
    YLOGA_FALSE(handle);

    // 组装数据
    uint8_t send_buf[8] = {0};
    send_buf[0]         = addr;
    send_buf[1]         = 3;
    memcpy(&send_buf[2], y_utils_big_little_swap((uint8_t *) &reg, 2), 2);
    memcpy(&send_buf[4], y_utils_big_little_swap((uint8_t *) &num, 2), 2);
    uint16_t crc16 = y_utils_get_crc16(send_buf, 6);  // 获取 crc16
    memcpy(&send_buf[6], &crc16, 2);                  // crc16

    // 发送请求数据
    if (handle->write) {
        return handle->write(send_buf, sizeof(send_buf)) ? true : false;
    }

    return false;
}

/// @brief   异步写单个寄存器
/// @param   [in] handle                   句柄
/// @param   [in] addr                     地址
/// @param   [in] reg                      寄存器
/// @param   [in] data                     写入寄存器数据
/// @retval  true                          成功
/// @retval  false                         失败
bool y_modbus_master_write_reg_async(MODBUS_st *handle, uint8_t addr, uint16_t reg, uint16_t data) {

    // 断言
    YLOGA_FALSE(handle);

    // 组装数据
    uint8_t send_buf[8] = {0};
    send_buf[0]         = addr;
    send_buf[1]         = 6;
    memcpy(&send_buf[2], y_utils_big_little_swap((uint8_t *) &reg, 2), 2);
    memcpy(&send_buf[4], y_utils_big_little_swap((uint8_t *) &data, 2), 2);
    uint16_t crc16 = y_utils_get_crc16(send_buf, 6);  // 获取 crc16
    memcpy(&send_buf[6], &crc16, 2);                  // crc16

    // 发送请求数据
    if (handle->write) {
        return handle->write(send_buf, sizeof(send_buf)) ? true : false;
    }

    return false;
}

/// @brief   异步写多个寄存器
/// @param   [in] handle                   句柄
/// @param   [in] addr                     地址
/// @param   [in] reg                      寄存器
/// @param   [in] num                      寄存器个数
/// @param   [in] data                     写入寄存器数据
/// @retval  true                          成功
/// @retval  false                         失败
bool y_modbus_master_write_data_async(MODBUS_st *handle, uint8_t addr, uint16_t reg, uint16_t num, uint8_t *data) {

    // 断言
    YLOGA_FALSE(handle);
    YLOGA_FALSE(data);

    // 组装数据
    uint8_t send_buf[261] = {0};
    uint8_t size          = num * 2;
    send_buf[0]           = addr;
    send_buf[1]           = 16;
    memcpy(&send_buf[2], y_utils_big_little_swap((uint8_t *) &reg, 2), 2);
    memcpy(&send_buf[4], y_utils_big_little_swap((uint8_t *) &num, 2), 2);
    send_buf[6] = size;  // 寄存器值字节数
    memcpy(&send_buf[7], data, send_buf[6]);
    for (int i = 0; i < size / 2; ++i) {
        y_utils_big_little_swap((&send_buf[7 + (i * 2)]), 2);
    }
    uint16_t crc16 = y_utils_get_crc16(send_buf, 7 + size);  // 获取 crc16
    memcpy(&send_buf[7 + size], &crc16, 2);                  // crc16

    // 发送请求数据
    if (handle->write) {
        return handle->write(send_buf, 9 + size) ? true : false;
    }

    return false;
}

/// @brief   添加待处理数据
/// @param   [in] data                     数据指针
/// @param   [in] size                     数据大小
/// @retval  true                          成功
/// @retval  false                         失败
bool y_modbus_add_data(MODBUS_st *handle, uint8_t *data, uint16_t size) {

    // 断言
    YLOGA_FALSE(handle);

    if (size) {
        // YLOG_DATA("MODBUS RECV", data, size);
        if (y_ringbuf_get_free_size(handle->ringbuf) >= size) {
            return y_ringbuf_write(handle->ringbuf, data, size);
        } else {
            y_ringbuf_reset(handle->ringbuf);
        }
    }
    return false;
}

/// @brief   handle 初始化
/// @param   [in] type                     句柄
/// @return  handle 句柄
MODBUS_st *y_modbus_init(MODBUS_TYPE_e type) {

    // 创建句柄
    MODBUS_st *handle = (MODBUS_st *) Y_MALLOC(sizeof(MODBUS_st));
    YLOGA_NULL(handle);
    memset(handle, 0, sizeof(MODBUS_st));

    // 初始化时给默认参数
    handle->type    = type;
    handle->ringbuf = y_ringbuf_create(3072);  // 创建 ringbuf
    if (handle->ringbuf == NULL) {
        Y_FREE(handle);
        return NULL;
    }

    return handle;
}

/// @brief   modbus 任务处理
/// @param   [in] handle                   句柄
/// @param   [in] time_ms                  每次调用间隔时间
void y_modbus_process(MODBUS_st *handle, uint32_t time_ms) {

    // 断言
    YLOGA(handle);

    // 处理 ringbuf 中的数据
    if (handle->type == MODBUS_MASTER) {
        _y_modbus_master_process(handle);  // 主机处理数据
    } else if (handle->type == MODBUS_SLAVE) {
        _y_modbus_slave_process(handle);  // 从机处理数据
    }
}
